import type { SSMLRenderer } from '@/utils/ssml/index';
import { VTree, VNode, SSMLSpeakElement, SSMLRenderNode, SSMLProvider, SSMLTagEnum } from './type'
import { multiplePinyin } from '../pinyin-pro';
import { getMarked } from './utils';
import { hanReg, engReg } from '../regexp';

const getAttrStr = (attribs: Record<string, any>, provider?: SSMLProvider) => {
  return Object.keys(attribs).map(attr => {
    switch (provider) {
      case SSMLProvider.YUAN: {
        switch (attr) {
          case 'alphabet': {
            return `${attr}="pinyin"`
          }
          case 'interpret-as': {
            switch (attribs[attr]) {
              case 'id':
              case 'letter': {
                return `${attr}="letter"`
              }
              case 'number':
              case 'currency': {
                return `${attr}="number"`
              }
              default: {
                return `${attr}="${attribs[attr]}"`
              }
            }
          }
          case 'level': {
            return `${attr}="${attribs[attr]}"`
          }
          case 'time': {
            switch (attribs[attr]) {
              case '500ms': {
                return `level="1"`
              }
              case '1000ms':
              case '1s': {
                return `level="2"`
              }
              case '2000ms':
              case '2s': {
                return `level="3"`
              }
              default: {
                return `${attr}="${attribs[attr]}"`
              }
            }
          }
          default: {
            return `${attr}="${attribs[attr]}"`
          }
        }
      }
      case SSMLProvider.DEFAULT: {
        switch (attr) {
          case 'alphabet': {
            return `${attr}="py"`
          }
          case 'interpret-as': {
            switch (attribs[attr]) {
              case 'id':
              case 'letter': {
                return `${attr}="id"`
              }
              case 'number':
              case 'currency': {
                return `${attr}="currency"`
              }
              default: {
                return `${attr}="${attribs[attr]}"`
              }
            }
          }
          case 'level': {
            switch (attribs[attr]) {
              case '1': {
                return `time="500ms"`
              }
              case '2': {
                return `time="1s"`
              }
              case '3': {
                return `time="2s"`
              }
              default: {
                return `${attr}="${attribs[attr]}"`
              }
            }
          }
          case 'time': {
            return `${attr}="${attribs[attr]}"`
          }
          default: {
            return `${attr}="${attribs[attr]}"`
          }
        }
      }
      default: {
        return `${attr}="${attribs[attr]}"`
      }
    }
  }).join(' ')
}

export const vTree2ssml = (root: VTree, provider: SSMLProvider) => {
  if (root.type !== 'root') throw new Error('need root node')
  if (!Array.isArray(root.children)) throw new Error('children invalid')

  const vNode2ssml = (node: VNode, preNode: VNode) => {
    const { name, attribs = {}, data } = node

    const genSingleTagSSML = (attrStr: string) => {
      return `<${name}${attrStr ? ' ' : ''}${attrStr} />`;
    }
    const genDoubleTagSSML = (attrStr: string, text: string) => {
      return `<${name}${attrStr ? ' ' : ''}${attrStr}>${text}</${name}>`
    }

    if (node.type === 'tag') {
      let text = ''
      if (node.children) {
        for (let i = 0; i < node.children.length; i++) {
          text += vNode2ssml(node.children[i], node.children[i - 1])
        }
      }
      const attrStr = getAttrStr(attribs, provider)
      switch (name) {
        case SSMLTagEnum.SPEAK: {
          return genDoubleTagSSML(attrStr, text)
        }
        case SSMLTagEnum.SAY_AS: {
          // 对线上已经有的英文标记进行降级
          if (attribs['interpret-as'] === 'name') {
            return text
          }
          // 对线上已经有的英文连词标记进行降级
          if (attribs['interpret-as'] === 'id' && /-/.test(text)) {
            const sliceText = text.split('-') as [string, string]
            return genDoubleTagSSML(attrStr, sliceText[0]) + '-' + genDoubleTagSSML(attrStr, sliceText[1])
          }
          // 对线上已经有的负数标记进行降级
          if ((attribs['interpret-as'] === 'digits' || attribs['interpret-as'] === 'currency') && text.startsWith('-')) {
            return '-' + genDoubleTagSSML(attrStr, text.slice(1))
          }
          if (attribs['interpret-as']) {
            return genDoubleTagSSML(attrStr, text)
          } else {
            return text
          }
        }
        case SSMLTagEnum.PHONEME: {
          return attribs['alphabet'] === 'py' && attribs['ph'] ? genDoubleTagSSML(attrStr, text) : text
        }
        case SSMLTagEnum.BREAK: {
          // 元分身停顿之前不能是标点符号，因此用空格替换
          if (provider === SSMLProvider.YUAN) {
            if (!preNode) return ' '
            if (preNode.type === 'tag') {
              if (preNode.name === 'break') return ' '
            } else {
              if (preNode.data && !hanReg.test(preNode.data) && !engReg.test(preNode.data)) {
                return ' '
              }
            }
          }
          return genSingleTagSSML(attrStr)
        }
        default: return text
      }
    } else {
      switch (data) {
        case '"': return '&quot;';
        case "'": return '&apos;';
        case '&': return '&amp;';
        case '<': return '&lt;';
        case '>': return '&gt;';
        default: return data
      }
    }
  }

  let ssml = ''
  for (let i = 0; i < root.children.length; i++) {
    ssml += vNode2ssml(root.children[i], root.children[i - 1])
  }
  return ssml
}

export const vTree2text = (root: VTree, options?: { break: string }) => {
  if (root.type !== 'root') throw new Error('need root node')
  if (!Array.isArray(root.children)) throw new Error('children invalid')

  const vNode2text = (node: VNode) => {
    if (node.type === 'tag') {
      let childStr = ''
      if (node.children) {
        for (const child of node.children) {
          childStr += vNode2text(child)
        }
      }
      return childStr
    } else {
      if (node.name === 'break') return options?.break ?? ' '
      return node.data
    }
  }

  let text = ''
  for (const child of root.children) {
    text += vNode2text(child)
  }
  return text
}

export const vTree2Rendener = (root: VTree) => {
  const { type, children } = root
  if (type !== 'root') throw new Error('need root node')
  if (!Array.isArray(children)) throw new Error('children invalid')
  const text = vTree2text(root)
  const multiPYDict = multiplePinyin(text)
  const vNode2Renderer = (node: VNode, renderers: SSMLRenderNode[]) => {
    const { name, data, attribs = {}, children } = node
    if (node.type === 'tag') {
      switch (name) {
        case SSMLTagEnum.BREAK: {
          renderers.push({
            id: renderers.length,
            type: name,
            // 停顿的内容使用空格来进行占位，避免检测出现问题
            data: ' ',
            dataType: name,
            attribs,
            marked: getMarked(node),
          })
          break;
        }
        case SSMLTagEnum.SPEAK: {
          console.error(`VNode ${name} should not be here`)
          break;
        }
        case SSMLTagEnum.PHONEME:
        case SSMLTagEnum.SAY_AS: {
          if (attribs['interpret-as'] === 'name') {
            if (children) {
              for (let i = 0; i < children.length; i++) {
                renderers.push({
                  id: renderers.length,
                  type: 'text',
                  attribs: {},
                  data: children[i].data,
                })
              }
            } else {
              console.error('children not exsit')
            }
          } else {
            renderers.push({
              id: renderers.length,
              type: name,
              data: children ? children.map(child => child.data).join('') : void 0,
              attribs,
              marked: getMarked(node),
            })
          }
          break;
        }
        default: {
          console.error(`VNode ${name} not support`)
        }
      }
    } else {
      if (data) {
        for (let i = 0; i < data.length; i++) {
          if (data[i] === '\n') {
            renderers.push({
              id: renderers.length,
              type: 'text',
              dataType: 'lineBreak',
              attribs: {},
              data: data[i],
              checked: true,
            })
          } else {
            renderers.push({
              id: renderers.length,
              type: 'text',
              attribs: {},
              data: data[i],
            })
          }
        }
      } else {
        console.error('data is empty')
      }
    }
  }

  const renderers: SSMLRenderNode[] = []
  const speak = children[0] as SSMLSpeakElement
  if (speak.children) {
    for (const child of speak.children) {
      vNode2Renderer(child, renderers)
    }
  }
  return {
    attribs: speak.attribs,
    children: renderers,
    point: renderers.length,
    multiPYDict,
  }
}

export const renderers2VTree = (renderer: SSMLRenderer) => {
  const vTree: VTree = {
    type: 'root',
    children: [],
  }
  const speak: SSMLSpeakElement = {
    type: 'tag',
    name: SSMLTagEnum.SPEAK,
    attribs: renderer.attribs,
    children: [],
  }

  const renderer2VNode = (render: SSMLRenderNode, vNodeList: VNode[]) => {
    const { type, attribs = {}, data } = render
    switch (type) {
      case 'text': {
        vNodeList.push({
          type: 'text',
          data,
        })
        break;
      }
      case SSMLTagEnum.BREAK: {
        vNodeList.push({
          type: 'tag',
          name: type,
          attribs,
        })
        break;
      }
      case SSMLTagEnum.SPEAK:
      case SSMLTagEnum.PHONEME:
      case SSMLTagEnum.SAY_AS: {
        vNodeList.push({
          attribs,
          children: [{
            type: 'text',
            data,
          }],
          name: type,
          type: 'tag',
        })
        break;
      }
      default: {
        console.error(`VNode ${type} not support`)
      }
    }
  }

  for (const child of renderer.children) {
    renderer2VNode(child, speak.children!)
  }
  vTree.children!.push(speak)
  return vTree
}
